/**
 * License would add here
 */

#ifndef UTILS_MACROS_H
#define UTILS_MACROS_H

#include <js_native_api.h>
#ifndef TERM_WHEN_FAILED
#define TERM_WHEN_FAILED 1
#endif

#include <node_api.h>

#define NAPI_UTILS_EXTERN \
    extern "C"

#include "log.h"

#if TERM_WHEN_FAILED
#define THROW_EXCEPTION(message)                                                                \
    do {                                                                                        \
        Logger(LogLevel::LOG_ERROR) << message << "\n    at "                                       \
                                << __func__ << " " << __FILE__ << ":" << __LINE__ << std::endl; \
        std::raise(SIGTERM);                                                                    \
    } while (0)
#else
#define THROW_EXCEPTION(message)                                                                \
    do {                                                                                        \
        Logger(LogLevel::LOG_ERROR) << message << "\n    at "                                       \
                                << __func__ << " " << __FILE__ << ":" << __LINE__ << std::endl; \
    } while (0)
#endif

#if TERM_WHEN_FAILED
#define THROW_JS_EXCEPTION(env, message)                                                         \
    do {                                                                                         \
        const napi_extended_error_info* errorInfo = nullptr;                                     \
        napi_get_last_error_info((env), &errorInfo);                                             \
        napi_value error = nullptr;                                                              \
        napi_get_and_clear_last_exception((env), &error);                                        \
        Logger(LogLevel::LOG_ERROR) << #message "\n"                                                 \
                                << "JsError: " << JSValue((env), error) << "\nErrorMessage: "    \
                                << (errorInfo->error_message == 0 ? "Empty error message"        \
                                                                  : errorInfo->error_message)    \
                                << "\n    at " << __func__ << " " << __FILE__ << ":" << __LINE__ \
                                << std::endl;                                                    \
        napi_throw(env, error);                                                                  \
        std::raise(SIGTERM);                                                                     \
    } while (0)
#else
#define THROW_JS_EXCEPTION(env, message)                                                         \
    do {                                                                                         \
        const napi_extended_error_info* errorInfo = nullptr;                                     \
        napi_get_last_error_info((env), &errorInfo);                                             \
        napi_value error = nullptr;                                                              \
        napi_get_and_clear_last_exception((env), &error);                                        \
        Logger(LogLevel::LOG_ERROR) << #message "\n"                                                 \
                                << "JsError: " << JSValue((env), error) << "\nErrorMessage: "    \
                                << (errorInfo->error_message == 0 ? "Empty error message"        \
                                                                  : errorInfo->error_message)    \
                                << "\n    at " << __func__ << " " << __FILE__ << ":" << __LINE__ \
                                << std::endl;                                                    \
        napi_throw(env, error);                                                                  \
    } while (0)
#endif

#define NAPI_STATUS_CALL(env, call)                          \
    do {                                                     \
        napi_status status = (call);                         \
        switch (status) {                                    \
        case napi_ok:                                        \
            break;                                           \
        case napi_pending_exception:                         \
            THROW_JS_EXCEPTION(env, napi_pending_exception); \
            break;                                           \
        default:                                             \
            THROW_EXCEPTION(ReadNapiStatus(status));         \
            break;                                           \
        }                                                    \
    } while (0)

NAPI_UTILS_EXTERN const char* ReadNapiStatus(napi_status status);

NAPI_UTILS_EXTERN const char* ReadNapiType(napi_valuetype type);

#define VALUE_TYPE_CHECK(cond, allowed, type)                                 \
    {                                                                         \
        if (!(cond)) {                                                        \
            THROW_EXCEPTION(allowed " expected, but \"" << ReadNapiType(type) \
                                                        << "\" is provided"); \
        }                                                                     \
    }

#endif /* UTILS_MACROS_H */
